package ltd.hxya.novel.book.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.rabbitmq.client.Channel;
import lombok.extern.slf4j.Slf4j;
import ltd.hxya.novel.book.dto.HomeBookRespDto;
import ltd.hxya.novel.book.entity.Book;
import ltd.hxya.novel.book.dto.BookChapterRespDto;
import ltd.hxya.novel.book.entity.BookIndex;
import ltd.hxya.novel.book.mapper.BookIndexMapper;
import ltd.hxya.novel.book.service.IBookIndexService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import ltd.hxya.novel.book.utils.resp.RestResp;
import ltd.hxya.novel.common.anno.OperLogAnno;
import ltd.hxya.novel.common.bean.ModuleEnum;
import ltd.hxya.novel.common.bean.OperEnum;
import ltd.hxya.novel.common.bean.Result;
import ltd.hxya.novel.common.constant.OperatorConstant;
import ltd.hxya.novel.common.constant.rabbit.RabbitConstant;
import ltd.hxya.novel.common.to.book.BookChapterTo;
import ltd.hxya.novel.common.utils.BaseUtils;
import ltd.hxya.novel.common.utils.PageUtils;
import ltd.hxya.novel.common.utils.RedisUtils;
import ltd.hxya.novel.common.vo.BookIndexVo;
import ltd.hxya.novel.common.constant.redis.RedisConstant;
import ltd.hxya.novel.common.exception.NovelGlobalException;
import ltd.hxya.novel.common.bean.ResultEnum;
import ltd.hxya.novel.common.vo.BookContentParam;
import ltd.hxya.novel.common.vo.PageParam;
import ltd.hxya.novel.common.vo.UserReadHistoryVo;
import ltd.hxya.novel.common.anno.RabbitAck;
import ltd.hxya.novel.entity.rowdata.DataPart;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.*;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.*;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import rx.Completable;

import java.io.IOException;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * <p>
 * 小说目录表 服务实现类
 * </p>
 *
 * @author hxya
 * @since 2022-09-13
 */
@Service
@Slf4j
public class BookIndexServiceImpl extends ServiceImpl<BookIndexMapper, BookIndex> implements IBookIndexService {

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    private RedisUtils redisUtils;

    @Autowired
    private BookIndexMapper bookIndexMapper;

    /**
     * 查询小说目录，并将小说目录添加到Redis中
     *
     * @param bookId
     * @param sort
     * @return
     */
    @Override
    public List<BookIndexVo> bookIndex(String bookId, Integer sort) {
        //查询Redis中是否有信息，如果没有信息，则查询数据库
        ValueOperations<String, String> valueOperations = redisTemplate.opsForValue();
        String json = valueOperations.get(RedisConstant.BOOK_INDEX_KEY + bookId);
        if (!StringUtils.isEmpty(json)) {

            //TODO 实现在Redis中查询到信息之后对得到的信息进行用户指定的方式排序
            List<BookIndexVo> bookIndices = JSON.parseObject(json, new TypeReference<List<BookIndexVo>>() {
            });
            return bookIndices;
        }
        if (sort == null) {
            sort = 1;
        }
        //根据bookId查询书籍目录
        QueryWrapper<BookIndex> bookIndexQueryWrapper = new QueryWrapper<>();
        bookIndexQueryWrapper.eq("book_id", bookId);
        if (sort == 1) {
            bookIndexQueryWrapper.orderByAsc("index_num");
        } else if (sort == 2) {
            bookIndexQueryWrapper.orderByDesc("index_num");
        }
        //将查询得到的信息添加到Redis中
        List<BookIndex> bookIndices = this.baseMapper.selectList(bookIndexQueryWrapper);
        //将bookIndices封装
        List<BookIndexVo> bookIndexVos = bookIndices.stream().map(bookIndex -> {
            BookIndexVo bookIndexVo = new BookIndexVo();
            BeanUtils.copyProperties(bookIndex, bookIndexVo);
            return bookIndexVo;
        }).collect(Collectors.toList());
        String bookIndexVosJson = JSON.toJSONString(bookIndexVos);

        valueOperations.set(RedisConstant.BOOK_INDEX_KEY + bookId, bookIndexVosJson, 1, TimeUnit.DAYS);

        return bookIndexVos;
    }


    @Override
    public Map<Integer, String> nextAndPreIndexId(BookContentParam param) {
        //从redis中查询出当前章节的信息
        ValueOperations<String, String> valueOperations = redisTemplate.opsForValue();
        String json = valueOperations.get(RedisConstant.BOOK_INDEX_KEY+param.getBookId());
        List<BookIndexVo> bookIndexVos = JSON.parseObject(json, new TypeReference<List<BookIndexVo>>() {
        });

        //通过param获得BookIndexVo信息
        BookIndexVo bookIndexVo = getBookIndexVo(param);

        Map<Integer, String> indexIdMap = new HashMap<>();

        //判断目前是否是第一章，如果是第一章，则并不获取上一章的信息
        if (!(bookIndexVo.getIndexNum()==0)){
            BookIndexVo preIndexVo = bookIndexVos.get(param.getIndexNum() - 1);
            indexIdMap.put(preIndexVo.getIndexNum(),preIndexVo.getId());
        }
        if (bookIndexVo.getIndexNum()<bookIndexVos.size()-1){
            BookIndexVo nexIndexVo = bookIndexVos.get(param.getIndexNum()+1);

            indexIdMap.put(nexIndexVo.getIndexNum(), nexIndexVo.getId());
        }

        return indexIdMap;
    }

    @Override
    public UserReadHistoryVo bookLastIndex(String bookId) {
        //查询该小说的第一章的索引
        ValueOperations<String, String> valueOperations = redisTemplate.opsForValue();
        String json = valueOperations.get(RedisConstant.BOOK_INDEX_KEY + bookId);
        List<BookIndexVo> bookIndexVos =null;
        if (StringUtils.isEmpty(json)){
             bookIndexVos = bookIndex(bookId, null);
        }else {
            bookIndexVos = JSON.parseObject(json, new TypeReference<List<BookIndexVo>>() {
            });
        }

        if (CollectionUtils.isEmpty(bookIndexVos)){
            //TODO 使用rabbit，发布任务
            rabbitTemplate.convertAndSend(RabbitConstant.CRAWL_BOOK_EXCHANGE,RabbitConstant.CRAWL_BOOK_ROUTING_KEY,bookId);
            throw new NovelGlobalException(ResultEnum.NETWORK_EXCEPTION.getCode(), ResultEnum.NETWORK_EXCEPTION.getMsg());
        }
        //获得小说第一章的id值
        BookIndexVo bookIndexVo = bookIndexVos.get(0);
        UserReadHistoryVo userReadHistoryVo = new UserReadHistoryVo();
        userReadHistoryVo.setIndexNum(bookIndexVo.getIndexNum());
        userReadHistoryVo.setBookId(bookId);
        userReadHistoryVo.setPreContentId(bookIndexVo.getId());
        return userReadHistoryVo;
    }

    @Override
    public List<BookChapterTo> saveBatchIndex(List<BookChapterTo> bookChapterTos) {
        //查询当前小说的最后一章的索引值
        String bookId = bookChapterTos.get(0).getBookId();
        long startTime = new Date().getTime();
        log.debug("开始插入小说章节索引："+bookId);
        List<BookIndexVo> bookIndexVos = bookIndex(bookId, null);
        Integer lastNum = 0;
        //判断是否已经有章节
        //TODO 当每一个错误，会出现章节错乱问题
        if (bookIndexVos.size()>0){
            BookIndexVo bookIndexVo = bookIndexVos.get(bookIndexVos.size() - 1);
            lastNum = bookIndexVo.getIndexNum();
        }

        List<BookIndex> bookIndices = new ArrayList<>();
        List<BookChapterTo> bookChapterTos1 = new ArrayList<>();

        for (BookChapterTo bookChapterTo : bookChapterTos) {
            BookIndex bookIndex = new BookIndex();

            bookIndex.setId(bookChapterTo.getIndexId());

            bookIndex.setBookId(bookChapterTo.getBookId());
            bookIndex.setIndexNum(bookChapterTo.getIndexNum()!=null? bookChapterTo.getIndexNum() : lastNum);

            bookIndex.setIndexName(bookChapterTo.getChapterName());
            bookIndex.setCreateTime(LocalDateTime.now());
            bookIndex.setUpdateTime(LocalDateTime.now());
            bookIndex.setWordCount(redisUtils.get(RedisConstant.BOOK_CONTENT_KEY+bookChapterTo.getIndexId()).length());
            lastNum++;
            bookIndices.add(bookIndex);
            bookChapterTo.setIndexId(bookChapterTo.getIndexId());
            bookChapterTos1.add(bookChapterTo);
        }

        // 删除更新Redis中的信息
        refreshRedisIndex(bookId);

        //将章节索引插入到数据库中
        this.saveBatch(bookIndices);
        log.debug("小说章节索引插入结束，耗时："+(new Date().getTime()-startTime)/1000.0+"秒");
        // 使用异步的方式矫正所有该小说的所有indexNum
        CompletableFuture.runAsync(()->{
            toCorrectIndexNum(bookChapterTos);
        });

        return bookChapterTos1;
    }

    private void toCorrectIndexNum(List<BookChapterTo> bookChapterTos) {
        List<Integer> indexNumList = bookChapterTos.stream().filter(bookChapterTo -> {
            Integer indexNum = bookChapterTo.getIndexNum();
            if (indexNum != null) {
                return true;
            }
            return false;
        }).map(bookChapterTo -> {
            return bookChapterTo.getIndexNum();
        }).collect(Collectors.toList());
        String bookId = bookChapterTos.get(0).getBookId();
        List<Integer> counts = bookIndexMapper.isRepeat(indexNumList,bookId);
        if (CollectionUtils.isEmpty(counts)){
            return;
        }
        //查询出所有的值，并对每一个值，修改
        List<BookIndex> bookIndices = bookIndexMapper.getIdAndNum(bookId);

        int index=0;
        for (BookIndex bookIndex : bookIndices) {
            bookIndex.setIndexNum(index);
            bookIndices.set(index,bookIndex);
            index++;
        }

        bookIndexMapper.updateIndexNum(bookIndices);

    }

    @Override
    public Page<BookIndex> indexList(String bookId,PageParam param) {
        //查询数据库信息
        QueryWrapper<BookIndex> bookIndexQueryWrapper = new QueryWrapper<>();
        bookIndexQueryWrapper.eq("book_id",bookId);
        Page<BookIndex> bookIndexPage = this.page(new Page<BookIndex>(param.getCurrent(), param.getSize()),bookIndexQueryWrapper);
        return bookIndexPage;
    }

    @Override
    public void changeIsVip(BookIndex bookIndex) {
        baseMapper.updateById(bookIndex);
    }

    @OperLogAnno(module = ModuleEnum.BOOK_INDEX,operator = OperEnum.DELETE,desc = "删除小说章节")
    @Override
    public void deleteIndecies(String bookId,String[] ids) {
        if (bookId==null){
            return;
        }
        if (ids==null){
            //如果ids为空，则清空所有的章节
            QueryWrapper<BookIndex> bookIndexQueryWrapper = new QueryWrapper<>();
            bookIndexQueryWrapper.eq("book_id",bookId);
            baseMapper.delete(bookIndexQueryWrapper);
            return;
        }
        if (ids.length<=0){
            return;
        }
        List<String> idList = Arrays.asList(ids);
        baseMapper.deleteBatchIds(idList);
    }

    @OperLogAnno(module = ModuleEnum.BOOK_INDEX,operator = OperEnum.DELETE,desc = "清空小说章节")
    @Override
    public void clearBookIndex(String bookId) {
        deleteIndecies(bookId,null);
    }

    @Override
    public List<BookIndex> saveIndex(List<DataPart> dataParts, Book book) {
        Integer maxNum = 0;
        String bookId = null;
        if (!ObjectUtils.isEmpty(book)){
            maxNum = bookIndexMapper.getMaxNum(book.getId());
            bookId = book.getId();
        }
        maxNum=maxNum==null?0:maxNum;
        bookId=bookId==null?dataParts.get(0).getBookId():bookId;
        //从数据库中查询章节的最后一章的信息
        //判断当前章节是否重复
        QueryWrapper<BookIndex> bookIndexQueryWrapper = new QueryWrapper<>();
        bookIndexQueryWrapper.eq("book_id",bookId);
        List<BookIndex> bookIndices = baseMapper.selectList(bookIndexQueryWrapper);
        dataParts.sort(new Comparator<DataPart>() {
            @Override
            public int compare(DataPart o1, DataPart o2) {
                return o1.getIndexNum()>o2.getIndexNum()?1:-1;
            }
        });
        List<BookIndex> indices = new ArrayList<>();
        List<String> indexNameList = bookIndices.stream().map(bookIndex -> bookIndex.getIndexName()).collect(Collectors.toList());
        Integer finalMaxNum = maxNum;
        Integer index =1;
        for (DataPart dataPart : dataParts) {
            if (indexNameList.contains(dataPart.getIndexName())){
                continue;
            }
            BookIndex bookIndex = new BookIndex();
            bookIndex.setBookId(bookId);
            bookIndex.setIndexName(dataPart.getIndexName());
            bookIndex.setId(IdWorker.getIdStr());
            bookIndex.setIndexNum(finalMaxNum +index);
            bookIndex.setWordCount(dataPart.getContent().length());
            bookIndex.setIsVip(0);
            indices.add(bookIndex);
            index++;
        }
        this.saveBatch(indices);
        return indices;
    }

    /**
     * 根据小说id，获取小说章节全部目录
     * @param bookId
     * @return
     */
    @Override
    public Result<List<BookChapterRespDto>> getListChapter(String bookId) {
        QueryWrapper<BookIndex> bookIndexQueryWrapper = new QueryWrapper<>();
        bookIndexQueryWrapper.eq("book_id",bookId);
        List<BookIndex> bookIndices = baseMapper.selectList(bookIndexQueryWrapper);
        List<BookChapterRespDto> collect = bookIndices.stream().map(v -> {
            BookChapterRespDto bookChapterRespDto = new BookChapterRespDto();
            bookChapterRespDto.setId(v.getId());
            bookChapterRespDto.setBookId(v.getBookId());
            bookChapterRespDto.setChapterNum(v.getIndexNum());
            bookChapterRespDto.setChapterName(v.getIndexName());
            bookChapterRespDto.setIsVip(v.getIsVip());
            bookChapterRespDto.setChapterUpdateTime(v.getUpdateTime());
            return bookChapterRespDto;
        }).collect(Collectors.toList());
        return Result.success(collect);
    }

    /**
     * 2、查询最新章节信息(根据章节的id，查询章节表，book_index表的处理)
     * @param lastChapterId  根据章节的id
     * @return
     */
    @Override
    public BookChapterRespDto getChapter(String lastChapterId) {
        BookIndex bookIndex = baseMapper.selectById(lastChapterId);
        if (ObjectUtils.isEmpty(bookIndex)){
            return null;
        }
        BookChapterRespDto bookChapterRespDto = new BookChapterRespDto();
        bookChapterRespDto.setId(lastChapterId);
        bookChapterRespDto.setBookId(bookIndex.getBookId());
        bookChapterRespDto.setChapterNum(bookIndex.getIndexNum());
        bookChapterRespDto.setChapterName(bookIndex.getIndexName());
        bookChapterRespDto.setChapterWordCount(bookIndex.getWordCount());
        bookChapterRespDto.setChapterUpdateTime(bookIndex.getUpdateTime());
        return bookChapterRespDto;
    }

    /**
     * 查询章节的总数
     * @param indexQueryWrapper
     * @return
     */
    @Override
    public Long selectCount(QueryWrapper<BookIndex> indexQueryWrapper) {
        return baseMapper.selectCount(indexQueryWrapper);
    }

    /**
     * 根据条件只获取一个的处理
     * @param queryWrapper
     * @return
     */
    @Override
    public BookIndex selectFirstBook(QueryWrapper<BookIndex> queryWrapper) {
        return baseMapper.selectOne(queryWrapper);
    }

    @Override
    public BookIndex selectOne(QueryWrapper<BookIndex> queryWrapper) {
        return baseMapper.selectOne(queryWrapper);
    }


    private void refreshRedisIndex(String bookId) {
        redisUtils.delete(RedisConstant.BOOK_INDEX_KEY+bookId);
        CompletableFuture.supplyAsync(()->{
            return bookIndex(bookId,null);
        });
    }


    private BookIndexVo getBookIndexVo(BookContentParam param) {
        ValueOperations<String, String> valueOperations = redisTemplate.opsForValue();
        String json = valueOperations.get(RedisConstant.BOOK_INDEX_KEY+param.getBookId());
        List<BookIndexVo> bookIndexVos =null;
        if (StringUtils.isEmpty(json)){
             bookIndexVos = bookIndex(param.getBookId(), null);
        }else {
            bookIndexVos = JSON.parseObject(json, new TypeReference<List<BookIndexVo>>() {
            });
        }
        //如果数据库中没有数据，抛出异常
        if (CollectionUtils.isEmpty(bookIndexVos)){
            throw new NovelGlobalException(ResultEnum.NOVEL_NOT_FREE.getCode(), ResultEnum.NOVEL_NOT_FREE.getMsg());
        }


        BookIndexVo bookIndexVo = bookIndexVos.get(param.getIndexNum());
        return bookIndexVo;
    }

    //删除小说中的所有章节
    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(RabbitConstant.DELETE_BOOK_QUEUE),
            exchange = @Exchange(RabbitConstant.DELETE_BOOK_EXCHANGE),
            key = RabbitConstant.DELETE_BOOK_ROUTING_KEY))
    @RabbitHandler
    @RabbitAck
    public void deleteBook(List<String> ids , Message message, Channel channel) throws IOException {
       /* List<String> ids = JSON.parseObject(idsJson, new TypeReference<List<String>>() {
        });*/
        bookIndexMapper.deleteByBookId(ids);


    }


}
